#![allow(unused_mut)]
#![allow(unused_variables)]
#![allow(unused_imports)]
#![allow(unreachable_code)]
#![allow(dead_code)]
#![allow(unused_must_use)]


#[macro_use]
extern crate dotenv_codegen;

use std::str;
use std::collections::HashSet ;
use std::net::SocketAddr ;
use std::env::var ;

use serde::{Serialize, Deserialize};
use avro_rs::{Schema, from_avro_datum} ;

use base64 ;


use tonic::{Request, Response, Status, Code} ;
use tonic::transport::{Server, Channel} ;
use xmeta::{AvroValidate, MetaValidateReply} ;
use xmeta::meta_server::{Meta, MetaServer} ;

use self::utils::* ;

use rust_embed::RustEmbed;

use dotenv::dotenv;

#[derive(RustEmbed)]
#[folder = "avsc"]
struct AvscAsset;


fn check_env() -> Result<(), String> {
    Ok(())
}

pub mod xmeta {
    tonic::include_proto!("meta") ;
}

#[derive(Default)]
pub struct MyMeta {}


#[tonic::async_trait]
impl Meta for MyMeta {
    async fn validate_avro(&self, validate_req: Request<AvroValidate>) -> Result<Response<MetaValidateReply>, Status> {
        let validate = validate_req.into_inner() ;
        let mut bs = zstd::decode_all(validate.body.as_slice()).map_err(|e| Status::unknown(format!("zstd decode error: {:?}", e)))? ;

        let schema_bs = AvscAsset::get("dc_sdk_dev.avsc").expect("dc_sdk_dev.avsc not found!") ;
        let schema_s = str::from_utf8(&schema_bs).expect("") ;
        let schema = Schema::parse_str(&schema_s).map_err(|e| Status::unknown(format!("schema parse error: {:?}", e)))? ;

        let datum = from_avro_datum(&schema, &mut bs.as_slice(), None).map_err(|e| Status::unknown(format!("avro read error: {:?}", e)))? ;


        Ok(Response::new(MetaValidateReply {
            code: 0, 
            msg: "success".to_string(), 
            // details: format!("{:#?}", datum)
            details: "aaaa".to_string()
        }))
    }
}

mod utils {
    use super::* ;

    pub fn code_status(code: u8, msg: &str) -> Status {
        Status::new(Code::from(code as i32), msg)
    }
    pub fn resolve_socket_addr(hosts: &str) -> Option<SocketAddr> {
        let ips = get_if_addrs::get_if_addrs().unwrap().iter()
            .map(|interface| interface.ip()).collect::<HashSet<_>>();
        hosts.split(",")
            .map(|x| x.replace("http://", ""))
            .filter_map(|x| x.parse::<SocketAddr>().ok())
            .filter(|x| ips.contains(&x.ip()))
            .nth(0)
    }

}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv().ok();

    let meta_server_hosts = var("META_SERVER_HOSTS").unwrap_or("http://127.0.0.1:3035".to_string()) ;
    if resolve_socket_addr(&meta_server_hosts).is_none() {
        println!("META_SERVER_HOSTS not match! skip") ;
        return Ok(()) ;
    }

    let addr: SocketAddr = "0.0.0.0:3035".parse().expect("web server address parser error!");
    println!("start server at {}", addr) ;

    Server::builder()
        .add_service(MetaServer::new(MyMeta::default()))
        .serve(addr)
        .await? ;
    
    Ok(())
}
